<!-- This project constructs the Helix "correlation payload", the set of files needed to run a set of tests,
     and then invokes sendtohelixhelp.proj for each test assembly and scenario combination to get Helix to
     invoke the tests.

     In the simple case, no scenarios are specified and the default CoreCLR optimization configuration
     is used to run the tests. If a set of comma-separated scenarios are specified in the `_Scenarios`
     property, then each test is run for each of the specified CoreCLR scenarios (e.g., DOTNET_JitStress=1;
     DOTNET_JitStressRegs=4; DOTNET_JitStress=2 + DOTNET_JitStressRegs=0x1000). The set of acceptable
     scenario names is defined and interpreted by src\tests\Common\testenvironment.proj.

    "RunInParallelForEachScenario" is the "root" target for this Project. It first creates the
    "correlation payload", which is the set of files used by all Helix submissions
    (which we compress into a single file).
-->
<Project Sdk="Microsoft.Build.NoTargets" InitialTargets="_SetTestArchiveRuntimeFile">

  <PropertyGroup>
    <BuildTargetFramework Condition="'$(BuildTargetFramework)' == ''">$(NetCoreAppCurrent)</BuildTargetFramework>

    <!-- Set the name of the scenario file. Note that this is only used in invocations where $(Scenario) is set
         (which is when this project is invoked to call the "CreateOneScenarioTestEnvFile" target). -->
    <TestEnvFileName Condition=" '$(TargetOS)' == 'windows' ">SetStressModes_$(Scenario).cmd</TestEnvFileName>
    <TestEnvFileName Condition=" '$(TargetOS)' != 'windows' ">SetStressModes_$(Scenario).sh</TestEnvFileName>

  </PropertyGroup>

  <!-- The Helix correlation payload file -->
  <Target Name="_SetTestArchiveRuntimeFile"
          Condition="'$(BuildTargetFramework)' == '$(NetCoreAppCurrent)'">
    <PropertyGroup>
      <TestArchiveRuntimeFile>$(TestArchiveRuntimeRoot)test-runtime-$(NetCoreAppCurrentBuildSettings).zip</TestArchiveRuntimeFile>
    </PropertyGroup>
  </Target>

  <Target Name="RunInParallelForEachScenario"
          AfterTargets="Build">
    <PropertyGroup>
      <!-- This specifies what properties are needed to be passed down as global properties to a child project invocation. -->
      <_PropertiesToPass>
        RuntimeFlavor=$(RuntimeFlavor);
        TargetArchitecture=$(TargetArchitecture);
        Configuration=$(Configuration);
        TargetOS=$(TargetOS);
        TargetRuntimeIdentifier=$(TargetRuntimeIdentifier);
        TestRunNamePrefixSuffix=$(TestRunNamePrefixSuffix);
        Creator=$(Creator);
        HelixAccessToken=$(HelixAccessToken);
        HelixTargetQueues=$(HelixTargetQueues);
        BuildTargetFramework=$(BuildTargetFramework);
        Scenarios=$(_Scenarios);
        SuperPmiCollect=$(_SuperPmiCollect);
        SuperPmiCollectionType=$(_SuperPmiCollectionType);
        SuperPmiCollectionName=$(_SuperPmiCollectionName)
      </_PropertiesToPass>
      <_DebuggerHosts Condition="'$(_DebuggerHosts)' == ''">$(DebuggerHost)</_DebuggerHosts>
      <_DebuggerHosts Condition="'$(_DebuggerHosts)' == ''">chrome</_DebuggerHosts>
    </PropertyGroup>

    <Message Condition="'$(_SuperPmiCollect)' == 'true'" Importance="High" Text="Doing SuperPMI collection: $(_SuperPmiCollectionType) $(_SuperPmiCollectionName)" />
    <Message Condition="'$(_Scenarios)' != ''" Importance="High" Text="Using _Scenarios: $(_Scenarios)" />
    <Message Condition="'$(_DebuggerHosts)' != ''" Importance="High" Text="Using _DebuggerHosts: $(_DebuggerHosts)" />
    <Message Importance="High" Text="Using Queues: $(HelixTargetQueues)" />
    <Message Importance="High" Text="BuildTargetFramework: $(BuildTargetFramework)" />
    <Message Importance="High" Text="TestArchiveTestsRoot: $(TestArchiveTestsRoot)" />
    <Message Importance="High" Text="TestArchiveRoot: $(TestArchiveRoot)" />
    <Message Importance="High" Text="TestArchiveRuntimeRoot: $(TestArchiveRuntimeRoot)" />
    <Message Condition="'$(TestArchiveRuntimeFile)' != ''" Importance="High" Text="TestArchiveRuntimeFile: $(TestArchiveRuntimeFile)" />

    <!-- Re-invoke MSBuild on this project to create the correlation payload -->
    <MSBuild Projects="$(MSBuildProjectFile)" Targets="PrepareCorrelationPayloadDirectory" Properties="$(_PropertiesToPass)" />

    <PropertyGroup>
      <PerScenarioProjectFile>$(MSBuildThisFileDirectory)sendtohelixhelp.proj</PerScenarioProjectFile>
    </PropertyGroup>

    <ItemGroup>
      <_Scenarios Include="$(_Scenarios.Split(','))" />

      <!-- MSBuild creates a new instance of the project for each %(_Scenarios.Identity) and can build them in parallel. -->
      <_BaseProjectsToBuild Include="$(PerScenarioProjectFile)" Condition="'%(_Scenarios.Identity)' != 'buildwasmapps' and '%(_Scenarios.Identity)' != 'buildiosapps' and '%(_Scenarios.Identity)' != 'wasmdebuggertests'">
        <AdditionalProperties>$(_PropertiesToPass);Scenario=%(_Scenarios.Identity);TestArchiveRuntimeFile=$(TestArchiveRuntimeFile)</AdditionalProperties>
        <AdditionalProperties Condition="'$(NeedsToBuildWasmAppsOnHelix)' != ''">%(_BaseProjectsToBuild.AdditionalProperties);NeedsToBuildWasmAppsOnHelix=$(NeedsToBuildWasmAppsOnHelix)</AdditionalProperties>
      </_BaseProjectsToBuild>
    </ItemGroup>

    <!-- For BuildWasmApps we want to build the project 4 times, with: TestUsingWorkloads=true, and TestUsingWorkloads=false and TestUsingWebcil=true and TestUsingWebcil=false-->
    <ItemGroup Condition="'@(_Scenarios -> AnyHaveMetadataValue('Identity', 'buildwasmapps'))' == 'true'">
      <_TestUsingWorkloadsValues Include="true;false" />
      <_TestUsingWebcilValues Include="true;false" Condition="'$(TargetOS)' == 'browser'" />

      <!-- now make the cartesian product of true and false values for two categories -->
      <_TestUsingCrossProductValuesTemp Include="@(_TestUsingWorkloadsValues)">
        <Workloads>%(_TestUsingWorkloadsValues.Identity)</Workloads>
      </_TestUsingCrossProductValuesTemp>
      <_TestUsingCrossProductValues Include="@(_TestUsingCrossProductValuesTemp)">
        <Webcil>%(_TestUsingWebcilValues.Identity)</Webcil>
      </_TestUsingCrossProductValues>

      <_BuildWasmAppsProjectsToBuild Include="$(PerScenarioProjectFile)">
        <AdditionalProperties>$(_PropertiesToPass);Scenario=BuildWasmApps;TestArchiveRuntimeFile=$(TestArchiveRuntimeFile);TestUsingWorkloads=%(_TestUsingCrossProductValues.Workloads);TestUsingWebcil=%(_TestUsingCrossProductValues.Webcil)</AdditionalProperties>
        <AdditionalProperties Condition="'$(NeedsToBuildWasmAppsOnHelix)' != ''">%(_BuildWasmAppsProjectsToBuild.AdditionalProperties);NeedsToBuildWasmAppsOnHelix=$(NeedsToBuildWasmAppsOnHelix)</AdditionalProperties>
      </_BuildWasmAppsProjectsToBuild>
    </ItemGroup>

    <ItemGroup Condition="'@(_Scenarios -> AnyHaveMetadataValue('Identity', 'wasmdebuggertests'))' == 'true'">
      <_DebuggerHostsItem Include="$(_DebuggerHosts.Split('/'))" />

      <_WasmDebuggerTestsProjectsToBuild Include="$(PerScenarioProjectFile)">
        <AdditionalProperties>$(_PropertiesToPass);Scenario=WasmDebuggerTests;TestArchiveRuntimeFile=$(TestArchiveRuntimeFile);DebuggerHost=%(_DebuggerHostsItem.Identity)</AdditionalProperties>
      </_WasmDebuggerTestsProjectsToBuild>
    </ItemGroup>

    <!-- For BuildWasmApps we want to build the project twice, with: TestUsingWorkloads=true, and TestUsingWorkloads=false -->
    <ItemGroup Condition="'@(_Scenarios -> AnyHaveMetadataValue('Identity', 'buildiosapps'))' == 'true'">
      <_TestUsingWorkloadsValues Include="false" />

      <_BuildiOSAppsProjectsToBuild Include="$(PerScenarioProjectFile)">
        <AdditionalProperties>$(_PropertiesToPass);Scenario=BuildiOSApps;TestArchiveRuntimeFile=$(TestArchiveRuntimeFile);TestUsingWorkloads=%(_TestUsingWorkloadsValues.Identity)</AdditionalProperties>
        <AdditionalProperties Condition="'$(NeedsToBuildAppsOnHelix)' != ''">%(_BuildiOSAppsProjectsToBuild.AdditionalProperties);NeedsToBuildAppsOnHelix=$(NeedsToBuildAppsOnHelix)</AdditionalProperties>
      </_BuildiOSAppsProjectsToBuild>
    </ItemGroup>

    <ItemGroup>
      <_ProjectsToBuild Include="@(_BuildWasmAppsProjectsToBuild);@(_WasmDebuggerTestsProjectsToBuild);@(_BuildiOSAppsProjectsToBuild);@(_BaseProjectsToBuild)" />
    </ItemGroup>

    <PropertyGroup>
      <_BuildInParallel>false</_BuildInParallel>
      <_BuildInParallel Condition=" '@(_ProjectsToBuild->Count())' &gt; '1' ">true</_BuildInParallel>
    </PropertyGroup>

    <!-- Invoke MSBuild once for each Scenario (because of the "batching" defined in "_ProjectsToBuild").
         Create the Helix work items and start the jobs. This is done by invoking the "Test" Helix target.
    -->
    <MSBuild Projects="@(_ProjectsToBuild)" Targets="Test" BuildInParallel="$(_BuildInParallel)" StopOnFirstFailure="false" />
  </Target>

  <Target Name="CreateOneScenarioTestEnvFile">
    <!-- This target creates one __TestEnv file for the single $(Scenario). -->
    <Error Condition="'$(Scenario)' == ''" Text="No Scenario specified" />

    <PropertyGroup>
      <TestEnvFilePath>$(NetCoreAppCurrentTestHostPath)$(TestEnvFileName)</TestEnvFilePath>
      <_targetsWindows Condition="'$(TargetOS)' == 'windows'">true</_targetsWindows>
    </PropertyGroup>

    <ItemGroup>
      <_ProjectsToBuild Include="$(RepoRoot)\src\tests\Common\testenvironment.proj">
        <Properties>Scenario=$(Scenario);TestEnvFileName=$(TestEnvFilePath);TargetsWindows=$(_targetsWindows)</Properties>
      </_ProjectsToBuild>
    </ItemGroup>

    <Message Importance="High" Text="Creating $(TestEnvFilePath) for scenario $(Scenario)" />

    <MSBuild Projects="@(_ProjectsToBuild)" Targets="CreateTestEnvFile" StopOnFirstFailure="true" />

    <Error Condition="!Exists('$(TestEnvFilePath)')" Text="File $(TestEnvFilePath) not found!" />
  </Target>

  <Target Condition="'$(Scenarios)' != '' and '$(TargetOS)' != 'browser' and '$(TargetOS)' != 'wasi' and '$(TargetOS)' != 'ios' and '$(TargetOS)' != 'iossimulator'" Name="CreateAllScenarioTestEnvFiles">
    <!-- This target creates one __TestEnv file for each of the scenarios in the $(Scenarios) comma-separated list. -->

    <Message Importance="High" Text="Creating per-scenario TestEnv files for scenarios $(Scenarios)" />

    <ItemGroup>
      <_Scenario Include="$(Scenarios.Split(','))" />
      <_ProjectsToBuild Include="$(MSBuildProjectFile)">
        <AdditionalProperties>Scenario=%(_Scenario.Identity)</AdditionalProperties>
      </_ProjectsToBuild>
    </ItemGroup>

    <MSBuild Projects="@(_ProjectsToBuild)" Targets="CreateOneScenarioTestEnvFile" StopOnFirstFailure="true" />
  </Target>

  <Target Name="_CollectRuntimeInputs">
    <!--
      We need to include all dlls in the runtime path as inputs to make it really incremental. If we use the root folder,
      if a dll is updated, the folder's timestamp is not updated, therefore skipped.
    -->
    <ItemGroup>
      <_RuntimeInput Include="$(NetCoreAppCurrentTestHostPath)**\*.dll" />

      <!-- Add the scenario TestEnv batch files -->
      <_RuntimeInput Condition="'$(Scenarios)' != '' and '$(TargetOS)' == 'windows'" Include="$(NetCoreAppCurrentTestHostPath)**\*.cmd" />
      <_RuntimeInput Condition="'$(Scenarios)' != '' and '$(TargetOS)' != 'windows'" Include="$(NetCoreAppCurrentTestHostPath)**\*.sh" />
    </ItemGroup>
  </Target>

  <Target Name="IncludeDumpDocsInTesthost"
          Condition="'$(RuntimeFlavor)' == 'CoreCLR'">
    <ItemGroup>
      <DebugDocsFiles Include="$(RepositoryEngineeringDir)testing\debug-dump-template.md" />
      <DebugDocsFiles Include="$(RepositoryEngineeringDir)testing\gen-debug-dump-docs.py" />

      <_RuntimeInputs Include="@(DebugDocsFiles)" />
    </ItemGroup>

    <Copy SourceFiles="@(DebugDocsFiles)"
          DestinationFolder="$(NetCoreAppCurrentTestHostPath)"
          SkipUnchangedFiles="true" />
  </Target>

  <Target Name="PrepareSuperPmiCollectCorrelationPayload"
          Condition="'$(SuperPmiCollect)' == 'true'"
          DependsOnTargets="ResolveRuntimeFilesFromLocalBuild">
    <!-- Copy Files needed for SuperPMI collection into a directory that will be added to the single, compressed file used as the correlation payload by Helix. -->
    <Message Importance="High" Text="Preparing SuperPMI collection payload directory" />

    <ItemGroup>
      <!-- We need superpmi.py and its dependencies; this is an over-approximation. The HelixCorrelationPayload directory is CoreRootDirectory, so copy the scripts there. -->
      <_SuperPmiScriptsFiles Include="$(RepoRoot)src\coreclr\scripts\*.py" />
    </ItemGroup>

    <Message Importance="High" Text="Copying $(RepoRoot)src\coreclr\scripts\*.py to $(NetCoreAppCurrentTestHostPath)spmi_scripts" />
    <MakeDir Directories="$(NetCoreAppCurrentTestHostPath)spmi_scripts" />
    <Copy SourceFiles="@(_SuperPmiScriptsFiles)" DestinationFolder="$(NetCoreAppCurrentTestHostPath)spmi_scripts" />

    <!-- Copy the runtime build into the correlation payload. The normal libraries test run takes the pieces of the runtime needed and puts them into the
         "testhost" directory structure (using the "build libs.pretest" build step). This doesn't include the SuperPMI shim or tools, and is also in a directory
         structure we don't don't control. So copy the whole runtime build to a directory we control. We probably only need a few things
         (superpmi, mcs, superpmi-shim-collector, clrjit), but it's easier to just copy everything.
    -->

    <Message Importance="High" Text="Copying Runtime binaries from $(CoreCLRArtifactsPath) to $(TestArchiveRoot)\coreclr" />
    <MakeDir Directories="$(NetCoreAppCurrentTestHostPath)coreclr" />
    <ItemGroup>
      <SuperPMICoreClrFiles Include="$(CoreCLRArtifactsPath)\**\*.*"/>
    </ItemGroup>
    <Copy SourceFiles="@(SuperPMICoreClrFiles)" DestinationFolder="$(TestArchiveRoot)coreclr\%(RecursiveDir)" />

    <ZipDirectory SourceDirectory="$(TestArchiveRoot)coreclr"
                  DestinationFile="$(TestArchiveRoot)coreclr.zip"
                  Overwrite="true" />
  </Target>

  <Target Name="CompressRuntimeDirectory"
          DependsOnTargets="IncludeDumpDocsInTesthost;_CollectRuntimeInputs"
          Inputs="@(_RuntimeInput);@(TestArchiveRuntimeDependency)"
          Outputs="$(TestArchiveRuntimeFile)"
          Condition="'$(TargetsMobile)' != 'true' and
                     '$(TestArchiveRuntimeFile)' != ''">
    <!-- Compress the test files, testhost, and per-scenario scripts into a single ZIP file for sending to the Helix machines. -->

    <Message Importance="High" Text="Compressing runtime directory" />

    <Message Importance="High" Text="Creating directory $(TestArchiveRuntimeRoot)" />
    <MakeDir Directories="$(TestArchiveRuntimeRoot)" />

    <ZipDirectory SourceDirectory="$(NetCoreAppCurrentTestHostPath)"
                  DestinationFile="$(TestArchiveRuntimeFile)"
                  Overwrite="true" />
  </Target>

  <!--
    Collect all the tasks needed to be run once, to prepare the Helix correlation payload directory.
    There's no actual work here; this target just causes its dependent targets to be run.
  -->
  <Target Name="PrepareCorrelationPayloadDirectory"
          DependsOnTargets="CreateAllScenarioTestEnvFiles;PrepareSuperPmiCollectCorrelationPayload;CompressRuntimeDirectory" >
    <Message Importance="High" Text="Correlation directory prepared" />
  </Target>

</Project>
